﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using ParsecParser.Data;
using ParsecParser.Helpers;
using ParsecParser.Display;

namespace ParsecParser
{
    [Serializable]
    public class CombatLog
    {
        public int LastFightIndex;
        public int RaidUserId { get; set; }
        public int EventCount { get; set; }
        public double CombatReEntryWindow = 3.5;
        public double StealthReEntryWindow = 16;
        public double ReivalWindowSeconds = 40;
        public double ReviveReEntryWindow = 16;
        public string CharacterName { get; set; }
        public string FileName { get; set; }
        public bool IsInCombat { get; set; }

        public DateTime CurrentLogDate { get; set; }
        public DateTime LastLocalReadTime = new DateTime();
        public DateTime? FloatingEndCombatTime = null;
        public DateTime? IgnoreCombatWindowStart = null;
        public DateTime? IgnoreCombatWindowEnd = null;
        public DateTime? ReviveWindowStart = null;
        public DateTime? ReviveWindowEnd = null;
        public DateTime? StealthWindowStart = null;
        public DateTime? StealthWindowEnd = null;

        public Parser Parser { get; set; }
        public LogState CurrentLogState { get; set; }
        public CombatStatistics LogStatistics { get; set; }

        private object FightsLock = new object();
        private Dictionary<int, CombatDetails> Fights { get; set; }

        private object TargetAliasLock = new object();
        private Dictionary<long, string> TargetAbilityNameAliases { get; set; }

        private object SourceAliasLock = new object();
        private Dictionary<long, string> SourceAbilityNameAliases { get; set; }

        private object NonFightEventsLock = new object();
        private List<CombatLogEvent> NonFightEvents { get; set; }

        //public event EventHandler<FightEndEventArgs> FightEnded;
        //public event EventHandler<FightStartEventArgs> FightStarted;
        
        #region Constructors
        public CombatLog(ParsecParser.Parser parser)
        {
            Parser = parser;
            NonFightEvents = new List<CombatLogEvent>();
            CurrentLogDate = DateTime.Today;
            LastFightIndex = 0;
            Fights = new Dictionary<int, CombatDetails>();
            LogStatistics = new CombatStatistics();
            CurrentLogState = new LogState();
            TargetAbilityNameAliases = new Dictionary<long, string>();
            SourceAbilityNameAliases = new Dictionary<long, string>();
            LastLocalReadTime = DateTime.Now;
        }
        public CombatLog(ParsecParser.Parser parser, string fileName)
        {
            NonFightEvents = new List<CombatLogEvent>();
            FileName = fileName;
            Parser = parser;
            CurrentLogDate = Parser.ParseDateFromFileName(fileName, false);
            LastFightIndex = 0;
            Fights = new Dictionary<int, CombatDetails>();
            LogStatistics = new CombatStatistics();
            CurrentLogState = new LogState();
            TargetAbilityNameAliases = new Dictionary<long, string>();
            SourceAbilityNameAliases = new Dictionary<long, string>();
            LastLocalReadTime = DateTime.Now;
        }
        public CombatLog()
        {
            Parser = new Parser();
            NonFightEvents = new List<CombatLogEvent>();
            CurrentLogDate = DateTime.Today;
            LastFightIndex = 0;
            Fights = new Dictionary<int, CombatDetails>();
            LogStatistics = new CombatStatistics();
            CurrentLogState = new LogState();
            TargetAbilityNameAliases = new Dictionary<long, string>();
            SourceAbilityNameAliases = new Dictionary<long, string>();
            LastLocalReadTime = DateTime.Now;
        }
        #endregion

        #region Public Methods
        public List<CombatLogEvent> AddLines(List<string> lines, out bool containsCombatStart)
        {
            if (Parser == null)
                Parser = new ParsecParser.Parser();
            List<CombatLogEvent> newEvents = new List<CombatLogEvent>();
            bool linesAdded = false;
            containsCombatStart = false;
            foreach (string s in lines)
            {
                CombatLogEvent ev = Parser.ParseLine(s, CurrentLogDate);
                bool thisLineCombatStart = false;
                ev = AddEvent(ev, out thisLineCombatStart);
                if (ev != null)
                {
                    newEvents.Add(ev);
                    if (thisLineCombatStart)
                        containsCombatStart = true;
                    linesAdded = true;
                }
            }
            if (linesAdded)
                LastLocalReadTime = DateTime.Now;
            return newEvents;
        }
        public CombatLogEvent AddEvent(CombatLogEvent ev)
        {
            bool x = false;
            return AddEvent(ev, out x);
        }
        private CombatLogEvent AddEvent(CombatLogEvent ev, out bool IsCombatStartEvent)
        {
            IsCombatStartEvent = false;
            bool raiseFightEndedEvent = false;
            bool resetThreat = false;
            if (ev != null)
            {
                EventCount++;
                ev.EventId = EventCount;
                ev.CharacterName = CharacterName;
                CurrentLogDate = ev.TimeStamp;
                if (string.IsNullOrEmpty(CharacterName) && ev.Source.IsPlayer && ev.Target.IsPlayer && !ev.Source.IsPlayerCompanion && !ev.Target.IsPlayerCompanion && (ev.Source.DisplayName == ev.Target.DisplayName))
                {
                    CharacterName = ev.Source.DisplayName;
                }
                ev.Source.SetIsThisPlayer(CharacterName);
                ev.Target.SetIsThisPlayer(CharacterName);
                if (ev.Source.IsThisPlayer && ev.EffectName.EntityId == CombatLogConstants.EffectNames.Revived && IsInReviveWindow(ev.TimeStamp))
                {
                    //player is revived within revival Window extend combat window
                    FloatingEndCombatTime = ev.TimeStamp.AddSeconds(this.ReviveReEntryWindow);
                    IgnoreCombatWindowStart = ev.TimeStamp;
                    IgnoreCombatWindowEnd = FloatingEndCombatTime;
                    ReviveWindowStart = null;
                    ReviveWindowEnd = null;
                }
                else if (ev.Source.IsThisPlayer && ev.EffectName.EntityId == CombatLogConstants.EffectNames.EnterCombat)
                {
                    bool validCombatStart = true; //assume it is valid
                    if (IsInCombat)
                    {
                        CombatDetails lf = GetFight(LastFightIndex);
                        if (lf != null && lf.CombatEndEvent == null) //no combat end event
                        {
                            validCombatStart = false;
                        }
                    }
                    if (validCombatStart) //ignore invalid combat start events
                    {
                        bool connectFight = FloatingEndCombatTime.HasValue && ev.TimeStamp.CompareTo(FloatingEndCombatTime.Value) <= 0;
                        if (!connectFight)
                        {
                            IsCombatStartEvent = true;
                            LastFightIndex++;
                            AddOrUpdateFight(LastFightIndex, new CombatDetails() { StartCombat = ev.TimeStamp, CharacterName = CharacterName, LogStateAtStart = new LogState() { CharacterName = this.CharacterName, GuardCount = CurrentLogState.GuardCount } });
                            ClearNameAliases();
                        }
                        else
                        {
                            resetThreat = true;
                        }
                    }
                    FloatingEndCombatTime = null;
                    IgnoreCombatWindowStart = null;
                    IgnoreCombatWindowEnd = null;
                    ReviveWindowStart = null;
                    ReviveWindowEnd = null;
                    IsInCombat = true;
                }
                else if (IsInCombat)
                {
                    if (FloatingEndCombatTime.HasValue && ev.TimeStamp.CompareTo(FloatingEndCombatTime.Value) >= 0) //fight ended and time ran out on floating end
                    {
                        //only end the fight if a combat end event has occured.
                        CombatDetails lf = GetFight(LastFightIndex);
                        if (lf != null && lf.CombatEndEvent != null) //no combat end event
                        {
                            raiseFightEndedEvent = true;
                            IsInCombat = false;
                        }
                        FloatingEndCombatTime = null;
                    }
                    else if (ev.EffectName != null && ev.Ability != null && ev.EffectName.EntityId == CombatLogConstants.EffectNames.AbilityActivate && ev.Ability.IsCombatDroppedAbility())
                    {
                        //check for stealth if it is detected extend EndCombatTime to 20 seconds and set IgnoreNextEnterCombat
                        OpenStealthRentryWindow(ev.TimeStamp);
                        FloatingEndCombatTime = ev.TimeStamp.AddSeconds(this.StealthReEntryWindow);
                        IgnoreCombatWindowStart = ev.TimeStamp;
                        IgnoreCombatWindowEnd = FloatingEndCombatTime;
                    }
                }
                //check if guard is applied or removed
                if (ev.Target.IsThisPlayer && ev.EffectType.EntityId == CombatLogConstants.EffectTypes.ApplyEffect && ev.EffectName.EffectIsGuard())
                {
                    if (CurrentLogState.GuardCount < 2)
                        CurrentLogState.GuardCount++;
                }
                if (ev.Target.IsThisPlayer && ev.EffectType.EntityId == CombatLogConstants.EffectTypes.RemoveEffect && ev.EffectName.EffectIsGuard())
                {
                    if (CurrentLogState.GuardCount > 0)
                        CurrentLogState.GuardCount--;
                }
                if (ev.EffectName.EntityId == CombatLogConstants.EffectNames.Heal)
                {
                    ev.EffectiveHeal = ev.Ability.CalculateEffectiveHeal(ev.Value, ev.Threat, CurrentLogState.IsGuarded);
                }
                if (IsInCombat)
                {
                    ev.FightIndex = LastFightIndex;
                    bool addSource = true;
                    bool addTarget = ev.Source.CompareTo(ev.Target) != 0;
                    CombatDetails currentFight = GetFight(LastFightIndex);
                    CombatLogEvent lastFightEvent = currentFight.GetLastEvent();
                    //only add timespan if we are not in the floating 1 sec window
                    if (FloatingEndCombatTime.HasValue == false && currentFight.EndCombat.HasValue == true) //if floating endcombattime is null and endcombat has value reset end combat time
                    {
                        currentFight.CombatEndEvent = null;
                        currentFight.EndCombat = null;
                    }
                    if (!currentFight.EndCombat.HasValue)//if endcombat doesnt have a value keep time running
                    {
                        if (lastFightEvent != null)
                            LogStatistics.CombatTimeSpan = LogStatistics.CombatTimeSpan.Add(ev.TimeStamp.Subtract(lastFightEvent.TimeStamp));
                        currentFight.CombatTimeSpan = ev.TimeStamp.Subtract(currentFight.StartCombat);
                    }
                    foreach(CombatLogParticipant clp in currentFight.GetParticipantList())
                    {
                        if (addSource && ev.Source.CompareTo(clp) == 0)
                        {
                            addSource = false;
                            if (clp.DisplayName == CombatLogParticipant.EmptyParticipantName && ev.Source.DisplayName != CombatLogParticipant.EmptyParticipantName)
                            {
                                clp.DisplayName = ev.Source.DisplayName; //update display name if it has been detected
                            }
                        }
                        if (addTarget && ev.Target.CompareTo(clp) == 0)
                        {
                            addTarget = false;
                            if (clp.DisplayName == CombatLogParticipant.EmptyParticipantName && ev.Target.DisplayName != CombatLogParticipant.EmptyParticipantName)
                            {
                                clp.DisplayName = ev.Target.DisplayName; //update display name if it has been detected
                            }
                        }
                        if (!addTarget && !addSource) //leave early if both are found
                            break;
                    }
                    if (addTarget)
                    {
                        currentFight.AddOrUpdateParticipant(ev.Target);
                        if (!ev.Target.IsPlayer && (currentFight.RaidEncounterDetail.RaidEncounterId == 0 || currentFight.RaidEncounterDetail.Mode == RaidEncounterDetails.DifficultyMode.Unknown || currentFight.RaidEncounterDetail.Players == RaidEncounterDetails.PlayerCount.Unknown))
                        {
                            RaidEncounterDetails newDetails = ev.Target.GetRaidEncounterDetails();
                            if (newDetails.RaidEncounterId > 0)
                                currentFight.RaidEncounterDetail = newDetails;
                        }
                    }
                    if (addSource)
                    {
                        currentFight.AddOrUpdateParticipant(ev.Source);
                        if (!ev.Source.IsPlayer && (currentFight.RaidEncounterDetail.RaidEncounterId == 0 || currentFight.RaidEncounterDetail.Mode == RaidEncounterDetails.DifficultyMode.Unknown || currentFight.RaidEncounterDetail.Players == RaidEncounterDetails.PlayerCount.Unknown))
                        {
                            RaidEncounterDetails newDetails = ev.Source.GetRaidEncounterDetails();
                            if (newDetails.RaidEncounterId > 0)
                                currentFight.RaidEncounterDetail = ev.Source.GetRaidEncounterDetails();
                        }
                    }
                    if (ev.Target.IsThisPlayer && (ev.EffectName.EntityId == CombatLogConstants.EffectNames.ExitCombat || ev.EffectName.EntityId == CombatLogConstants.EffectNames.Death || ev.EffectName.EntityId == CombatLogConstants.EffectNames.SafeLoginImmunity))
                    {
                        //LastEndCombatEventTime = ev.TimeStamp;
                        if (ev.EffectName.EntityId == CombatLogConstants.EffectNames.Death)
                            OpenRevivalWindow(ev.TimeStamp);
                        currentFight.CombatEndEvent = ev;
                        currentFight.EndCombat = ev.TimeStamp;
                        if (IsInCombatExitIgnoreWindow(ev.TimeStamp)) //already waiting for the end of a window
                        {
                            FloatingEndCombatTime = IgnoreCombatWindowEnd;
                        }
                        else //need to set window ending
                        {
                            FloatingEndCombatTime = ev.TimeStamp.AddSeconds(this.CombatReEntryWindow);
                            if (IsInStealthRentryWindow(ev.TimeStamp) && StealthWindowEnd.Value.CompareTo(FloatingEndCombatTime) > 0) //in stealth re entry window so set new end time unless combat re-entry is longer
                            {
                                FloatingEndCombatTime = StealthWindowEnd;
                            }
                            IgnoreCombatWindowStart = ev.TimeStamp;
                            IgnoreCombatWindowEnd = FloatingEndCombatTime;
                        }
                    }
                    if (ev.EffectName != null)
                    {
                        if (ev.Source.IsThisPlayer)
                        {
                            if (ev.EffectName.EntityId == CombatLogConstants.EffectNames.Damage)
                            {
                                currentFight.LastOutgoingDamage = ev;
                                currentFight.DamageOut += ev.Value;
                                LogStatistics.DamageOut += ev.Value;
                                if (ev.Threat != 0)
                                {
                                    long effectiveThreat = CombatLogHelpers.GetEffectiveThreat(currentFight.Threat, ev.Threat);
                                    currentFight.Threat += effectiveThreat;
                                    LogStatistics.Threat += effectiveThreat;

                                    CombatLogParticipant targetParticipant = currentFight.GetParticipant(ev.Target.GetUniqueKey());
                                    if (!ev.Target.IsPlayer && targetParticipant != null)
                                    {
                                        targetParticipant.IsEnemy = true;
                                        targetParticipant.TotalThreat += CombatLogHelpers.GetEffectiveThreat(targetParticipant.TotalThreat, ev.Threat);
                                        targetParticipant.TotalDamage += ev.Value;
                                    }
                                }
                                
                                if (!currentFight.IsPvpFight && currentFight.RaidEncounterDetail.RaidEncounterId == 0 && ev.Source.IsPlayer && !ev.Source.IsPlayerCompanion && ev.Target.IsPlayer && !ev.Target.IsPlayerCompanion && ev.Target.CompareTo(ev.Source) != 0)
                                {
                                    currentFight.IsPvpFight = true; //mark as pvp fight if event is damage and source and target are players
                                }

                                ev.Ability.DisplayName = FindNameAlias(ev.Ability.EntityId, true, ev.Ability.DisplayName);
                                ActivateDetails lastActivate = currentFight.GetActivate(ev.Ability.EntityId);
                                if (lastActivate != null)
                                {
                                    ev.HitIndex = lastActivate.CurrentHitCount;
                                    lastActivate.CurrentHitCount++;
                                    lastActivate.TotalHitCount++;
                                    lastActivate.MaxHitCount = lastActivate.MaxHitCount < lastActivate.CurrentHitCount ? lastActivate.CurrentHitCount : lastActivate.MaxHitCount;
                                    string currentEnemyId = ev.Target.IsPlayer ? ev.Target.DisplayName : ev.Target.UniqueId.ToString();
                                    if (string.IsNullOrEmpty(lastActivate.LastEnemyHitId))
                                        lastActivate.LastEnemyHitId = currentEnemyId;
                                    else if (lastActivate.LastEnemyHitId != currentEnemyId)
                                        lastActivate.IsMultiTarget = true;
                                }
                            }
                            else if (ev.EffectName.EntityId == CombatLogConstants.EffectNames.Heal)
                            {
                                if (ev.Target.IsPlayer || ev.Target.IsPlayerCompanion) //only count heals that heal players or companions
                                {
                                    currentFight.HealOut += ev.Value;
                                    currentFight.EffectiveHealOut += ev.EffectiveHeal;
                                    LogStatistics.HealOut += ev.Value;
                                    LogStatistics.EffectiveHealOut += ev.EffectiveHeal;
                                }
                                if (ev.Threat != 0)
                                {
                                    long effectiveThreat = CombatLogHelpers.GetEffectiveThreat(currentFight.Threat, ev.Threat);
                                    currentFight.Threat += effectiveThreat;
                                    LogStatistics.Threat += effectiveThreat;
                                    currentFight.HealThreat += CombatLogHelpers.GetEffectiveThreat(currentFight.HealThreat, ev.Threat);
                                    List<string> keys = new List<string>();
                                    foreach(var p in currentFight.GetParticipantList())
                                    {
                                        if (!p.IsPlayer && p.IsEnemy)
                                        {
                                            p.TotalThreat += CombatLogHelpers.GetEffectiveThreat(p.TotalThreat, ev.Threat);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (ev.EffectName.EntityId == CombatLogConstants.EffectNames.AbilityActivate)
                                {
                                    currentFight.Activates++;
                                    LogStatistics.Activates++;
                                    //currentFight.ActivateList.Remove(ev.Ability.EntityId);
                                    ActivateDetails lastActivate = currentFight.GetActivate(ev.Ability.EntityId);
                                    if (lastActivate != null)
                                    {
                                        lastActivate.TimeStamp = ev.TimeStamp;
                                        lastActivate.LastEnemyHitId = null;
                                        lastActivate.TotalActivationCount++;
                                        lastActivate.CurrentHitCount = 0;
                                    }
                                    else
                                    {
                                        lastActivate = new ActivateDetails();
                                        lastActivate.TimeStamp = ev.TimeStamp;
                                        lastActivate.CurrentHitCount = 0;
                                        lastActivate.TotalActivationCount = 1;
                                        currentFight.AddOrUpdateActivate(ev.Ability.EntityId, lastActivate);
                                    }
                                    if (ev.Ability.IsShieldAbility())
                                    {
                                        currentFight.AbsorbShieldsCast++;
                                        LogStatistics.AbsorbShieldsCast++;
                                    }
                                }
                                if (ev.EffectType != null && ev.Ability != null)
                                {
                                    if (ev.EffectType.EntityId == CombatLogConstants.EffectTypes.ApplyEffect && ev.Ability.DisplayName != ev.EffectName.DisplayName)
                                    {
                                        AddAlias(ev.EffectName.EntityId, true, ev.Ability.DisplayName, ev.EffectName.DisplayName);
                                    }
                                }
                                if (ev.Threat != 0 || resetThreat)
                                {
                                    if (resetThreat)
                                        ev.Threat = currentFight.Threat * -1;
                                    long effectiveThreat = CombatLogHelpers.GetEffectiveThreat(currentFight.Threat, ev.Threat);
                                    currentFight.Threat += effectiveThreat;
                                    LogStatistics.Threat += effectiveThreat;

                                    if (resetThreat)
                                    {
                                        currentFight.GetParticipantList().ForEach(x => { x.TotalThreat = 0; });
                                    }
                                    else
                                    {
                                        CombatLogParticipant targetParticipant = currentFight.GetParticipant(ev.Target.GetUniqueKey());
                                        if (!ev.Target.IsPlayer && targetParticipant != null)
                                        {
                                            targetParticipant.IsEnemy = true;
                                            targetParticipant.TotalThreat += CombatLogHelpers.GetEffectiveThreat(targetParticipant.TotalThreat, ev.Threat);
                                        }
                                    }
                                }
                            }
                        }
                        if (ev.Target.IsThisPlayer)
                        {
                            if (ev.EffectName.EntityId == CombatLogConstants.EffectNames.Damage)
                            {
                                currentFight.DamageIn += ev.Value;
                                LogStatistics.DamageIn += ev.Value;
                                currentFight.AbsorbedIn += ev.AbsorbedValue;
                                LogStatistics.AbsorbedIn += ev.AbsorbedValue;

                                ev.Ability.DisplayName = FindNameAlias(ev.Ability.EntityId, false, ev.Ability.DisplayName);

                                CombatLogParticipant sourceParticipant = currentFight.GetParticipant(ev.Source.GetUniqueKey());
                                if (!ev.Source.IsPlayer && sourceParticipant != null)
                                {
                                    sourceParticipant.IsEnemy = true;
                                }
                            }
                            else if (ev.EffectName.EntityId == CombatLogConstants.EffectNames.Heal)
                            {
                                currentFight.HealIn += ev.Value;
                                LogStatistics.HealIn += ev.Value;
                            }
                            else
                            {
                                if (ev.EffectType != null && ev.EffectName != null && ev.EffectType.EntityId == CombatLogConstants.EffectTypes.ApplyEffect && ev.EffectName.IsShieldEffect())
                                {
                                    currentFight.AbsorbShieldsCast++;
                                    LogStatistics.AbsorbShieldsCast++;
                                }
                                if (ev.EffectType != null && ev.Ability != null)
                                {
                                    if (ev.EffectType.EntityId == CombatLogConstants.EffectTypes.ApplyEffect && ev.Ability.DisplayName != ev.EffectName.DisplayName)
                                    {
                                        AddAlias(ev.EffectName.EntityId, false, ev.Ability.DisplayName, ev.EffectName.DisplayName);
                                    }
                                }
                            }
                        }
                        if (ev.Source.IsPlayerCompanion && ev.Source.CompanionOwner == CharacterName)
                        {
                            currentFight.CompanionInFight = true;
                            currentFight.Companion = ev.Source;
                            //companion is source
                            if (ev.EffectName.EntityId == CombatLogConstants.EffectNames.Damage)
                            {
                                currentFight.CompanionDamageOut += ev.Value;
                            }
                            else if (ev.EffectName.EntityId == CombatLogConstants.EffectNames.Heal)
                            {
                                currentFight.CompanionHealOut += ev.Value;
                                currentFight.CompanionEffectiveHealOut += ev.EffectiveHeal;
                            }

                            if (ev.Threat != 0)
                            {
                                long effectiveThreat = CombatLogHelpers.GetEffectiveThreat(currentFight.Threat, ev.Threat);
                                currentFight.CompanionThreat += effectiveThreat;
                            }
                        }
                        if (ev.Target.IsPlayerCompanion && ev.Target.CompanionOwner == CharacterName)
                        {
                            currentFight.CompanionInFight = true;
                            currentFight.Companion = ev.Target;
                            //companion is target
                            if (ev.EffectName.EntityId == CombatLogConstants.EffectNames.Damage)
                            {
                                currentFight.CompanionDamageIn += ev.Value;
                                currentFight.CompanionAbsorbedIn += ev.AbsorbedValue;
                            }
                            else if (ev.EffectName.EntityId == CombatLogConstants.EffectNames.Heal)
                            {
                                currentFight.CompanionHealIn += ev.Value;
                            }
                        }
                    }
                    currentFight.AddEvent(ev);
                }
                else
                {
                    NonFightEvents.Add(ev);
                }
                if (raiseFightEndedEvent)
                {
                    CombatDetails lf = GetFight(LastFightIndex);
                    CombatLogEvent fee = null;
                    if (lf != null) //no combat end event
                        fee = lf.CombatEndEvent;
                    //OnFightEnded(new FightEndEventArgs() { FightIndex = LastFightIndex, FightEndEvent = fee });
                }
                //if (fireCombatStartEvent)
                //{
                //    //OnFightStarted(new FightStartEventArgs() { FightIndex = LastFightIndex, FightStartEvent = ev });
                //}
                return ev;
            }
            return null;
        }
        public void EndCurrentFight()
        {
            //if we were in combat
            if (IsInCombat && FloatingEndCombatTime.HasValue)
            {
                DateTime fec = FloatingEndCombatTime.Value;
                IsInCombat = false;
                FloatingEndCombatTime = null;
                if (LastFightIndex > 0)
                {
                    CombatDetails c = null;
                    if (Fights.Count > 0)
                    {
                        c = GetFight(LastFightIndex);
                        if (c != null)
                        {
                            if (c.CombatEndEvent == null)
                                c.CombatEndEvent = new CombatLogEvent() { TimeStamp = c.EndCombat ?? fec, EffectName= new CombatLogEntity("Combat Timed Out",0), EffectType = new CombatLogEntity("Combat Timed Out",0)  }; //set combat end event if combat timed out
                        }
                    }
                }
            }
            ClearNameAliases();
        }
        #endregion

        //#region Event Methods
        //protected virtual void OnFightEnded(FightEndEventArgs e)
        //{
        //    EventHandler<FightEndEventArgs> handler = FightEnded;
        //    if (handler != null)
        //    {
        //        handler(this, e);
        //    }
        //}
        //protected virtual void OnFightStarted(FightStartEventArgs e)
        //{
        //    EventHandler<FightStartEventArgs> handler = FightStarted;
        //    if (handler != null)
        //    {
        //        handler(this, e);
        //    }
        //}
        //#endregion

        #region Helper Methods
        private bool IsInCombatExitIgnoreWindow(DateTime checkTime)
        {
            if (!IgnoreCombatWindowStart.HasValue || !IgnoreCombatWindowEnd.HasValue)
                return false;
            else
                return IgnoreCombatWindowStart.Value.CompareTo(checkTime) <= 0 && IgnoreCombatWindowEnd.Value.CompareTo(checkTime) >= 0;
        }
        private bool IsInReviveWindow(DateTime checkTime)
        {
            if (!ReviveWindowStart.HasValue || !ReviveWindowEnd.HasValue)
                return false;
            else
                return ReviveWindowStart.Value.CompareTo(checkTime) <= 0 && ReviveWindowEnd.Value.CompareTo(checkTime) >= 0;
        }
        private bool IsInStealthRentryWindow(DateTime checkTime)
        {
            if (!StealthWindowStart.HasValue || !StealthWindowEnd.HasValue)
                return false;
            else
                return StealthWindowStart.Value.CompareTo(checkTime) <= 0 && StealthWindowEnd.Value.CompareTo(checkTime) >= 0;
        }
        private void OpenRevivalWindow(DateTime startTime)
        {
            ReviveWindowStart = startTime;
            ReviveWindowEnd = startTime.AddSeconds(ReivalWindowSeconds);
        }
        private void OpenStealthRentryWindow(DateTime startTime)
        {
            StealthWindowStart = startTime;
            StealthWindowEnd = startTime.AddSeconds(StealthReEntryWindow);
        }
        #endregion

        #region Collection Methods
        public List<FightDisplayItem> GetFightDisplayItems()
        {
            lock (FightsLock)
            {
                return Fights.Select(x => new FightDisplayItem(x.Key, x.Value)).ToList();
            }
        }
        public List<CombatDetails> GetFightList()
        {
            lock (FightsLock)
            {
                return Fights.Values.ToList();
            }
        }
        public CombatDetails GetFight(int index)
        {
            CombatDetails fight = null;
            lock (FightsLock)
            {
                Fights.TryGetValue(index, out fight);
            }
            return fight;
        }
        public void AddOrUpdateFight(int key, CombatDetails c)
        {

            if (key >= 0 && c != null)
            {
                lock (FightsLock)
                {
                    Fights[LastFightIndex] = c;
                }
            }
        }
        private void ClearNameAliases()
        {
            lock (TargetAliasLock)
            {
                TargetAbilityNameAliases.Clear();
            }
            lock (SourceAliasLock)
            {
                SourceAbilityNameAliases.Clear();
            }
        }
        private string FindNameAlias(long abilityId, bool isSource, string originalName)
        {
            string newName = null;
            if (isSource)
            {
                lock (SourceAliasLock)
                {
                    SourceAbilityNameAliases.TryGetValue(abilityId, out newName);
                }
            }
            else
            {
                lock (TargetAliasLock)
                {
                    TargetAbilityNameAliases.TryGetValue(abilityId, out newName);
                }
            }
            return newName ?? originalName;
        }
        private void RemoveAlias(long effectNameId, bool isSource)
        {
            if (isSource && SourceAbilityNameAliases.ContainsKey(effectNameId))
            {
                lock (SourceAliasLock)
                {
                    SourceAbilityNameAliases.Remove(effectNameId);
                }
            }
            else if (TargetAbilityNameAliases.ContainsKey(effectNameId))
            {
                lock (TargetAliasLock)
                {
                    TargetAbilityNameAliases.Remove(effectNameId);
                }
            }
        }
        private void AddAlias(long effectNameId, bool isSource, string abilityName, string effectName)
        {
            string newName = string.Format("{0}: {1}", abilityName, effectName);
            if (isSource)
            {
                lock (SourceAliasLock)
                {
                    SourceAbilityNameAliases[effectNameId] = newName;
                }
            }
            else
            {
                lock (TargetAliasLock)
                {
                    TargetAbilityNameAliases[effectNameId] = newName;
                }
            }
        }
        #endregion
    }
}
